package pers.cz.testNg;

import cn.hutool.core.date.DateUtil;
import entity.DataBaseParamter;
import pers.cz.encrypt.DesUtil;
import pers.cz.kettle.DataSyncUtil;
import pers.cz.mapper.*;
import pers.cz.message.MessageType;
import pers.cz.message.MessageUtil;
import pers.cz.model.*;
import pers.cz.queue.AutoRequestQueue;
import pers.cz.http.HttpUtils;
import pers.cz.spring.SpringContextUtil;
import pers.cz.sql.SqlUtils;
import pers.cz.tools.TextTable;
import pers.cz.websocket.MessagePushSocketUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.testng.Reporter;
import org.testng.annotations.AfterTest;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.*;

/**
 * 自定义测试
 * @program: PostGirl-panent
 * @description: AutoTestNg
 * @author: Cheng Zhi
 * @create: 2021-11-13 18:25
 **/
public class AutoTestNg {

    Logger logger= LoggerFactory.getLogger(AutoTestNg.class);

    private List<ArrayList<ArrayList>> arrayLists;

    private String userId;
    private Map<Integer, Boolean> resultMap = new HashMap();

    @DataProvider(name = "Data")
    public Object[][] init() {
        // 初始化策略工厂
        ParamMethodFactory paramMethodFactory = new ParamMethodFactory();
        // 初始化准备数据
        String idsAndAddress = (String) AutoRequestQueue.getHeadQueue();
        String address = "";
        String[] split = idsAndAddress.split("\\^");
        address = split[1];
        List<Integer> idsIntger = new ArrayList<>();
        String idsString = split[0];
        userId = split[2];
        String defaultDataBaseId = split[4];
        String[] split1 = idsString.split(",");
        for (String ids : split1) {
            resultMap.put(Integer.valueOf(ids), true);
            idsIntger.add(Integer.valueOf(ids));
        }
        if (AutoRequestQueue.deleteHeadQueue() && logger.isDebugEnabled()) {
            logger.debug("删除队首成功");
        }

        Object[][] objects = new Object[1][];

        int idx = 0;
        for (int s= 1; s < idsIntger.size() + 1; s++) {

            Integer testCaseId = idsIntger.get(s-1);

            // 获取检查点
            List<AutoTestCaseCheck> listAutoTestCaseChecks = SpringContextUtil.getBean(AutoTestCaseCheckMapper.class).selectByTestCaseId(Integer.valueOf(testCaseId));

            AutoTestCase autoTestCase = SpringContextUtil.getBean(AutoTestCastMapper.class).selectByPrimaryKey(testCaseId);
            Integer testCaseTemplateId = autoTestCase.getTestCaseTemplateId();
            Integer publicTemplateId = autoTestCase.getPublicTemplateId();

            // 1、获取自定义模板
            AutoTestCaseTemplate autoTestCasetTemplate = SpringContextUtil.getBean(AutoTestCaseTemplateMapper.class).selectByPrimaryKey(testCaseTemplateId);
            String autoTestCasetTemplateStr = autoTestCasetTemplate.getTemplate();

            // 2、获取自定义参数
            List<AutoTestCaseParams> listAutoTestCaseParams = SpringContextUtil.getBean(AutoTestCaseParamsMapper.class).selectByTestCaseId(testCaseId);

            Reporter.log("自定义测试案例参数替换前：" + autoTestCasetTemplateStr);
            // 3、参数替换获取到真正的请求

            // 4、排列组合计算出所有案例：
            List<String[]> testCaseLists = new ArrayList<>();
            List<String> testCaseNameLists = new ArrayList<>();
            for (AutoTestCaseParams autoTestCaseParams : listAutoTestCaseParams) {

                String params = autoTestCaseParams.getParam();
                // 这里要分为三种情况来处理 1、普通参数，ext为null, 2、ext1为code, 3、ext1为sql
                if (autoTestCaseParams.getExt1() == null) {
                    autoTestCaseParams.setExt1("single");
                }
                String ext1 = autoTestCaseParams.getExt1();
                // 根据参数情况返回对应结果
                IMethodJudge methodImpl = paramMethodFactory.getMethodImpl(ext1);
                List<String[]> strings = methodImpl.splitParam(autoTestCaseParams.getId(), params);
                testCaseLists.addAll(strings);
                testCaseNameLists.add(autoTestCaseParams.getParamName());
            }

            if (testCaseLists.size() == 1) {
                // 如果案例只有一个参数就没有必要排列组合了
                List list1 = new ArrayList<>();
                List list2 = new ArrayList();
               for (String a : testCaseLists.get(0)) {

                    List list = new ArrayList();
                    list.add(a);
                    list2.add(list);
                }

                list1.add(list2);
                this.arrayLists = list1;
            } else {
                // 排列组合
                this.doExchange(testCaseLists);
            }

            // 二维数组扩容
            objects = Arrays.copyOf(objects, objects.length + this.arrayLists.get(0).size());

            // 5、获取公共模板
            AutoPublicTemplate autoPublicTemplate = SpringContextUtil.getBean(AutoPublicTemplateMapper.class).selectByPrimaryKey(publicTemplateId);
            String autoPublicTemplateStr = autoPublicTemplate.getTemplate();
            Reporter.log("公共参数替换前：" + autoPublicTemplateStr);
            // 1.1参数替换
            // 1.2 查询公共入参
            List<AutoPublicParams> listAutoPublicParams = SpringContextUtil.getBean(AutoPublicParamsMapper.class).selectByTestCaseId(testCaseId);

            Map publicParamsNamesMap = new HashMap();
            for (AutoPublicParams autoPublicParams : listAutoPublicParams) {
                publicParamsNamesMap.put(autoPublicParams.getParamName(),autoPublicParams.getParam());
            }

            String autoPublicRequest = replaceParam(autoPublicTemplateStr, publicParamsNamesMap);
            Reporter.log("公共参数替换后：" + autoPublicRequest);

            for (int i = 0; i<this.arrayLists.get(0).size(); i++) {

                Map<String,String> testCaseParamsNamesMap = new HashMap<String,String>();
                for (int j = 0; j < testCaseNameLists.size(); j++ ) {
                    testCaseParamsNamesMap.put(testCaseNameLists.get(j),this.arrayLists.get(0).get(i).get(j).toString());
                }

                String name = "";
                for (String key : testCaseParamsNamesMap.keySet()) {
                    name = name + "--" + testCaseParamsNamesMap.get(key);
                }

                String testCaseRequst = replaceParam(autoTestCasetTemplateStr, testCaseParamsNamesMap);
                String realRequest = "{" + autoPublicRequest + "," + testCaseRequst + "}";
                Object[] object = new Object[6];
                object[0] = realRequest;
                object[1] = address;
                object[2] = autoTestCase.getCaseName() + "(" + name + ")"; // 案例名称
                object[3] = String.valueOf(testCaseId);
                object[4] = listAutoTestCaseChecks;
                object[5] = defaultDataBaseId;
                objects[idx] = object;
                idx ++;
            }
        }

        return objects;
    }

    /**
     * 测试用例
     */
    @Test(dataProvider = "Data")
    public void testCase(String request, String url, String testCaseName, String testCaseId, List<AutoTestCaseCheck> listAutoTestCaseChecks, String defaultDataBaseId) {
        // 执行案例前获取并执行sql
        List<AutoTestCaseCheckSql> autoTestCaseCheckSqls = SpringContextUtil.getBean(AutoTestCaseCheckSqlMapper.class).selectByTestCaseId(testCaseId);

        // toto 暂时写死数据库
        Reporter.log("接口调用前数据状态如下：");
        Map<String, List<Map<String, Object>>> beforeDbResultMap = beforeInterfaceCall(autoTestCaseCheckSqls, Integer.valueOf(defaultDataBaseId));

        logger.debug("自动化测试请求报文： " + request);
        String response = HttpUtils.getResultByPost(request, url);
        Reporter.log("真实响应结果：" + response);
        Reporter.log("接口调用后数据状态如下：");
        Map<String, List<Map<String, Object>>> afterDbResultMap = afterInterfaceCall(autoTestCaseCheckSqls, beforeDbResultMap, Integer.valueOf(defaultDataBaseId));

        // 比较并标记两次执行差异

        // 循环获取checkPonit
        for (AutoTestCaseCheck autoTestCaseCheck : listAutoTestCaseChecks) {

            Boolean isPass = true;
            String checkPoint = autoTestCaseCheck.getCheckPoint(); // 检查点
            String assertionType = autoTestCaseCheck.getAssertionType(); // 断言类型
            Reporter.log("检查点：" + autoTestCaseCheck.getCheckPointName());
            try {
                if (assertionType.equals(AssertionTypeCode.CONTENT_CHECK)) {
                    Reporter.log("包含检查开始");
                    isPass = MyCheck.contentCheck(response, checkPoint);
                    Reporter.log("包含检查：" + isPass );
                }

                if (assertionType.equals(AssertionTypeCode.NOT_NULL_CHECK)) {
                    Reporter.log("不为空检查开始");
                    isPass = MyCheck.notNullCheck(response, checkPoint);
                    Reporter.log("不为空检查：" + isPass );
                }

                if (assertionType.equals(AssertionTypeCode.EXCEPTION_CHECK)) {
                    Reporter.log("异常检查开始");
                    isPass = MyCheck.exceptionCheck(response, checkPoint);
                    Reporter.log("异常检查：" + isPass );
                }

                if (assertionType.equals(AssertionTypeCode.JSONPATH_CHECK)) {

                    Reporter.log("表达式检查开始" + checkPoint);
                    isPass = MyCheck.jsonPathCheck(response, checkPoint);
                    Reporter.log("表达式检查：" + isPass );
                }

                if (assertionType.equals(AssertionTypeCode.CUSTOM_EXPR_CHECK)) {
                    Reporter.log("自定义表达式检查开始："  + checkPoint);
                    isPass = MyCheck.customExprCheck(beforeDbResultMap, afterDbResultMap, request, response, checkPoint);
                    Reporter.log("表达式检查：" + isPass );
                }
            } catch (Exception e) {
                isPass = false;
                resultMap.put(Integer.valueOf(testCaseId),false); // 这里改为失败
                MyAssert.check(isPass, e);
                return;
            }

            MyAssert.check(isPass, null);
        }

    }

    @AfterTest
    public void updateTableData() {

        for (Integer testCaseId : resultMap.keySet()) {

            String content = "";
            AutoTestCase autoTestCase = new AutoTestCase();
            autoTestCase.setId(testCaseId);
            autoTestCase.setUpdateTime(DateUtil.date());
            if (resultMap.get(testCaseId)) {
                autoTestCase.setExt1("0");
                content = "notify|执行成功,请刷新后再下载报告";
            } else {
                autoTestCase.setExt1("1");
                content = "notify|执行失败,请刷新后再下载报告";

            }

            SpringContextUtil.getBean(AutoTestCastMapper.class).updateByPrimaryKeySelective(autoTestCase);

            MessageUtil.insertNotifyMessage(content, MessageType.getName("notify"), "未读", userId);

            // 发送实时消息
            MessagePushSocketUtils.sendInfo(content,Integer.valueOf(userId));
        }
    }

    /**
     * 接口调用前执行
     * @param autoTestCaseCheckSqls
     * @param dataBaseId
     */
    private Map<String, List<Map<String, Object>>> beforeInterfaceCall(List<AutoTestCaseCheckSql> autoTestCaseCheckSqls, Integer dataBaseId) {
        Map<String, List<Map<String, Object>>> resultMap = new HashMap<String, List<Map<String, Object>>>();
        for (AutoTestCaseCheckSql autoTestCaseCheckSql : autoTestCaseCheckSqls) {
            List<Map<String, Object>> mapList = execSql(autoTestCaseCheckSql.getRunSql(), dataBaseId);
            Reporter.log("数据库执行sql：" + autoTestCaseCheckSql.getRunSql());
            printTableHtml(mapList, null, "before");
            resultMap.put(autoTestCaseCheckSql.getExt1().toUpperCase(), mapList);
        }
        return resultMap;
    }

    /**
     * 接口调用后执行
     * @param autoTestCaseCheckSqls
     * @param dataBaseId
     */
    private Map<String, List<Map<String, Object>>> afterInterfaceCall(List<AutoTestCaseCheckSql> autoTestCaseCheckSqls, Map<String, List<Map<String, Object>>> beforeResutMap, Integer dataBaseId) {
        Map<String, List<Map<String, Object>>> resultMap = new HashMap<String, List<Map<String, Object>>>();
        for (AutoTestCaseCheckSql autoTestCaseCheckSql : autoTestCaseCheckSqls) {
            List<Map<String, Object>> mapList = execSql(autoTestCaseCheckSql.getRunSql(), dataBaseId);
            Reporter.log("数据库执行sql：" + autoTestCaseCheckSql.getRunSql());
            String tableName = autoTestCaseCheckSql.getExt1().toUpperCase();
            List<Map<String, Object>> beforeTableList = beforeResutMap.get(tableName);
            // 用本次调用结果和上次结果做比对，将不一致的进行标记
            List<Map> differentList = compareSqlResultBeforeAndAfter(mapList, beforeTableList);
            printTableHtml(mapList, differentList, "after");
            resultMap.put(autoTestCaseCheckSql.getExt1().toUpperCase(), mapList);
        }

        return resultMap;
    }

    /**
     * 比较并获取变化的数据行
     * @param afterMapList
     * @param beforeMapList
     * @return
     */
    private List<Map> compareSqlResultBeforeAndAfter(List<Map<String, Object>> afterMapList, List<Map<String, Object>> beforeMapList) {

        List<Map> differentList = new ArrayList<Map>();
        for (Map map : afterMapList) {
            if (!beforeMapList.contains(map)) {
                differentList.add(map);
            }
        }

        return differentList;
    }

    /**
     * 替换模板中的参数
     * @param jsonTemplate
     * @param paramNames
     * @return
     */
    private String replaceParam(String jsonTemplate, Map<String,String> paramNames) {

        if (!jsonTemplate.contains("$")) {
            return jsonTemplate;
        }

        for (String paramName : paramNames.keySet()) {

            jsonTemplate = jsonTemplate.replaceAll("\\$\\{".concat(paramName).concat("\\}"),paramNames.get(paramName));
        }

        return jsonTemplate;
    }

    private List<Map<String, Object>> execSql(String sql, int dataBaseId) {

        DatabaseDtl databaseDtl = SpringContextUtil.getBean(DatabaseDtlMapper.class).selectByPrimaryKey(dataBaseId);

        // 获取执行参数
        DataBaseParamter paramter = new DataBaseParamter();
        paramter.setIp(databaseDtl.getIp());
        paramter.setType(databaseDtl.getType());
        paramter.setPort(String.valueOf(databaseDtl.getPort()));
        paramter.setConnectionName(databaseDtl.getConnectionName());
        paramter.setDataBaseName(databaseDtl.getDatabaseName());
        paramter.setUserName(databaseDtl.getUsername());
        paramter.setPassword(DesUtil.decode(databaseDtl.getPassword()));
        List<Map> results = null;
        try {
            return DataSyncUtil.executeSqlToMap(paramter, sql);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    /**
     * 排列组合算法：先两个排列组合，然后递归
     * @param arrayLists
     */
    public void doExchange(List arrayLists){

        int len=arrayLists.size();
        // 判断数组size是否小于2，如果小于说明已经递归完成了，否则你们懂得的，不懂？断续看代码
        if (len<2){
            this.arrayLists=arrayLists;
            return;
        }
        // 拿到第一个数组
        int len0;
        if (arrayLists.get(0) instanceof String[]){
            String[] arr0= (String[]) arrayLists.get(0);
            len0=arr0.length;
        }else {
            len0=((ArrayList<String>)arrayLists.get(0)).size();
        }

        // 拿到第二个数组
        String[] arr1= (String[]) arrayLists.get(1);
        int len1=arr1.length;

        // 计算当前两个数组一共能够组成多少个组合
        int lenBoth=len0*len1;

        // 定义临时存放排列数据的集合
        ArrayList<ArrayList<String>> tempArrayLists=new ArrayList<>(lenBoth);

        // 第一层for就是循环arrayLists第一个元素的
        for (int i=0;i<len0;i++){
            // 第二层for就是循环arrayLists第二个元素的
            for (int j=0;j<len1;j++){
                // 判断第一个元素如果是数组说明，循环才刚开始
                if (arrayLists.get(0) instanceof String[]){
                    String[] arr0= (String[]) arrayLists.get(0);
                    ArrayList<String> arr=new ArrayList<>();
                    arr.add(arr0[i]);
                    arr.add(arr1[j]);
                    // 把排列数据加到临时的集合中
                    tempArrayLists.add(arr);
                }else {
                    // 到这里就明循环了最少一轮啦，我们把上一轮的结果拿出来继续跟arrayLists的下一个元素排列
                    ArrayList<ArrayList<String>> arrtemp= (ArrayList<ArrayList<String>>) arrayLists.get(0);
                    ArrayList<String> arr=new ArrayList<>();
                    for (int k=0;k<arrtemp.get(i).size();k++){
                        arr.add(arrtemp.get(i).get(k));
                    }
                    arr.add(arr1[j]);
                    tempArrayLists.add(arr);
                }
            }
        }

        // 这是根据上面排列的结果重新生成的一个集合
        List newArrayLists=new ArrayList<>();
        // 把还没排列的数组装进来，看清楚i=2的喔，因为前面两个数组已经完事了，不需要再加进来了
        for (int i=2;i<arrayLists.size();i++){
            newArrayLists.add(arrayLists.get(i));
        }
        // 记得把我们辛苦排列的数据加到新集合的第一位喔，不然白忙了
        newArrayLists.add(0,tempArrayLists);

        doExchange(newArrayLists);
    }


    /**
     * 打印表格数据
     * @param maps
     */
    private void printTable(List<Map> maps) {
        // 获取标题
        TextTable textTable = new TextTable();
        textTable.setSbcMode(false);
        for (Map map : maps) {
            Set<String> set = map.keySet();
            String[] objects = set.toArray(new String[set.size()]);
            textTable.addHeader(objects);
            break;
        }


        for (Map<String, Object> map : maps) {
            List<String> list = new LinkedList<String>();
            for (String key : map.keySet()) {
                list.add(map.get(key) == null ? "" : map.get(key).toString());
            }
            textTable.addBody(list.toArray(new String[list.size()]));
        }
        // textTable.printToConsole();
        textTable.print(new TextTable.CustomPrintCallBack() {
            @Override
            public void print(TextTable textTable) {
                String s = textTable.toString().replaceAll("\\n", "<br>");
                s = s.replace(" ", "&nbsp;");
                Reporter.log(s,false);
            }
        });
    }

    /**
     * 打印表格数据
     * @param maps
     */
    private void printTableHtml(List<Map<String, Object>> maps, List<Map> differentList, String type) {
        StringBuilder html = new StringBuilder();
        if (type.equals("after") && (differentList == null || differentList.size() == 0)) {
            html.append("<span style=\"color:#ff9900;\">数据状态无变化！</span>");
            Reporter.log(html.toString());
            return;
        }

        html.append("<table border='1' style=\"font-size:10px;\">");
        // 标题行
        for (Map map : maps) {
            html.append("<tr>");
            for (Object key : map.keySet()) {
                html.append("<td>").append(key).append("</td>");
            }
            html.append("</tr>");
            break;
        }

        // 数据行
        for (Map<String, Object> map : maps) {
            boolean putColour = false;
            if (differentList != null) {
                // 如果要打印的内容中包含了变化后的数据行，则进行着色
                putColour = differentList.contains(map);
            }

            if (putColour) {
                html.append("<tr style=\"background-color: #ff9900;\">");
            } else {
                html.append("<tr>");
            }

            for (String key : map.keySet()) {
                html.append("<td>").append(map.get(key)).append("</td>");
            }

            html.append("</tr>");
        }
        html.append("</table>");

        Reporter.log(html.toString());
    }

    public static void main(String[] args) {
/*        String busiCode = "7060";
        String template = "{\"sOper\":{\"so_nbr\":\"WY2110230101206018\",\"busi_code\":${busiCode},\"so_mode\":${soMode},\"so_date\":\"2021-10-23 01:01:20\",\"charge_flag\":1,\"op_id\":999990099,\"region_code\":21,\"county_code\":21,\"org_id\":999990102,\"notify_flag\":0}}";
        String[] newTemplate = template.split("\\$");

        for (int i=1; i<newTemplate.length; i++) {
            int start = newTemplate[i].indexOf("{") + 1;
            int end = newTemplate[i].indexOf("}");
            String substring = newTemplate[i].substring(start, end);
            System.out.println(substring);
        }*/

        String[] strings = new String[10];
        for (int i=0; i<5; i++) {
            strings = Arrays.copyOf(strings, strings.length + 10);
        }
        System.out.println(strings.length);

    }
}
